/*
 * ResManager.cpp
 *
 *  Created on: 24 oct. 2010
 *      Author: seeme
 */


#include "ResCache.h"
#include "Resources/ResourceFactory.h"
#include <queue>
#include <string>
#include <fstream>
#include "../LogManager/Log.hpp"


using namespace std;

void ResCache::clear(){
	m_resourceMap.clear();
	m_nextResourceHandle = "";
	m_currentUsedMemory = 0;
	m_maximumMemory = 0;
	m_packageDir = "";
}

ResCache::ResCache(){
	clear();
}

bool ResCache::create(unsigned int nMaxSize){
	clear();
	setMaximumMemory(nMaxSize);
	return true;
}


void ResCache::destroy(){
	for(ResMapItor itor = m_resourceMap.begin(); itor != m_resourceMap.end(); ++itor)
	{
			delete ((*itor).second);
	}
	m_resourceMap.clear();
	clear();
}


bool ResCache::setMaximumMemory(size_t nMem){
	m_maximumMemory = nMem;
	return checkForOverallocation();
}


bool ResCache::reserveMemory(size_t nMem){
	addMemory(nMem);
	if(!checkForOverallocation())
		return false;
	return true;
}


bool ResCache::destroyResource(HANDLE rhUniqueID){
	ResMapItor itor = m_resourceMap.find(rhUniqueID);
	if(itor == m_resourceMap.end()){
		//Error, not found
		return false;
	}

	if(itor->second->getReferenceCount() > 0){
		return false;
	}

	if(itor->second->getSize() > 0){
		itor->second->destroy();
	}

	return true;
}



Resource* ResCache::getResource(HANDLE rhUniqueID){
	//If it's alread loaded, return it
	ResMapItor itor = m_resourceMap.find(rhUniqueID);
	if(itor == m_resourceMap.end()){
		//Error, not found
		Log::e("ResCache") << "Resource not found " << rhUniqueID;
		return NULL;
	}

	//Is it loaded?
	if((*itor).second->getSize() == 0){
		//Not loaded yet, load it
		(*itor).second->create();
		m_currentUsedMemory += (*itor).second->getSize();
	}

	//1up on the ref count
	//(*itor).second

	(*itor).second->upRefCount();

	//Check memory state
	checkForOverallocation();

	//Log::i("ResCache") << "Looking for " << rhUniqueID << ", loaded " << (*itor).second->m_filePath << " counter = " << (*itor).second->getReferenceCount();
	return (*itor).second;
}

void ResCache::releaseResource(HANDLE uniqHandle){
	//Desincrement the ref counter
	m_resourceMap.find(uniqHandle)->second->downRefCount();
}

bool ResCache::checkForOverallocation(){
	if(m_currentUsedMemory > m_maximumMemory){
		//Over memory, need to free a bit;

		//Remove the resources that are currently not used (refcount = 0)

		priority_queue<Resource*, vector<Resource*>, ptr_greater<Resource*> > PriQueue;

		//Look for the most suitable resource.
		for(ResMapItor itor = m_resourceMap.begin(); itor != m_resourceMap.end(); ++itor){
			if(!itor->second->getSize() == 0 && itor->second->getReferenceCount() == 0)
				PriQueue.push(itor->second);
		}

		while((!PriQueue.empty()) && (m_currentUsedMemory > m_maximumMemory)){
			unsigned int nDisposalSize = PriQueue.top()->getSize();
			Resource* res = PriQueue.top();
			//Log::i("MemoryCache") << "Destroying " << res->m_name << " count = " << res->getReferenceCount() << " size = " << res->getSize();
			res->destroy();
			if(PriQueue.top()->getSize() == 0)
				m_currentUsedMemory -= nDisposalSize;
			PriQueue.pop();
		}

	}
	return true;
}

void ResCache::setPackageDir(const char* dir){
	m_packageDir = dir;

	size_t pos = m_packageDir.find_last_of("/");
	if(pos != m_packageDir.length()-1){
		m_packageDir.append("/");
	}
}

vector<string> ResCache::split(const string source, const char sep){
	vector<string> rep;
	string s;
	for(unsigned int i = 0; i < source.length(); ++i){
		if(source[i] != sep){
			s.push_back(source[i]);
		}else{
			rep.push_back(s);
			s.clear();
		}
	}
	return rep;
}

void ResCache::addResource(HANDLE name, Resource* pointer){
	if(pointer){
		m_resourceMap.insert(ResMapPair(name, pointer));
	}
}

void ResCache::loadPackage(const char* dir){
	std::string direction(m_packageDir + dir);
	Log::i("ResCache") << "Loading package " << direction;

	ifstream file(direction.c_str());
	vector<string> vect;

	if (file){
		string line;

		while(getline(file, line)){
			if(line.substr(0, 1) != "#"){

				//TEST
				vect = split(line, '|');

				//boost::split(vect, line, boost::is_any_of("|"));

				//vect now contains the data needed for the ressource creation.
				//Here, the factory comes handy..
				m_resourceMap.insert(ResMapPair(vect[0], m_resFactory.buildResource(vect[0], vect[1], m_packageDir+vect[2])));
			}
		}
	}else{
		Log::e("ResCache") << "Error Loading Package";
	}
}
